﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Windy.OpenLink.Names;
using System.Diagnostics;

namespace Windy.OpenLink
{
    public static class NameManager
    {
        #region Const

        public const char PrimaryNameSeperator = '.';
        public const char ExtensionNameSeperator = ':';

        private static readonly char[] NameSpeartorArray = new char[] { PrimaryNameSeperator, ExtensionNameSeperator };
        private static readonly char[] PrimaryNameSeperatorArray = new char[] { PrimaryNameSeperator };
        private static readonly char[] ExtensionNameSeperatorArray = new char[] { ExtensionNameSeperator };

        public const string HierarchyNameTemplate = "{0}{2}{1}";

        #endregion

        #region Feature Methods

        #region StringAppend

        public static string Child(this string name, string childName)
        {
            if (name == null)
                throw new ArgumentNullException("name", "name is null.");
            if (childName == null)
                throw new ArgumentNullException("childName", "childName is null.");

            if (!name.VerifyName())
                throw NameException.CreateKnownException(NameException.ExceptionReason.InvalidNodeName);

            if (!childName.VerifyNodeName())
                throw NameException.CreateKnownException(NameException.ExceptionReason.InvalidNodePrimaryName);

            return name.AppendNamePart(childName, PrimaryNameSeperator);
        }

        public static string ChildExtension(this string name, string childPrimaryName, string extension = null)
        {
            return name.Child(childPrimaryName.Extension(extension));
        }

        public static string Extension(this string name, string extension)
        {
            if (name == null)
                throw new ArgumentNullException("name", "name is null.");

            if (!name.VerifyName())
                throw NameException.CreateKnownException(NameException.ExceptionReason.InvalidNodeName);

            if (extension == null)
                return name;

            if (!extension.VerifyExtensionName())
                throw NameException.CreateKnownException(NameException.ExceptionReason.InvalidNodePrimaryName);

            return name.AppendNamePart(extension, ExtensionNameSeperator);
        }

        #endregion

        #region ToName

        public static string ToFullName(this NameItem item)
        {
            if (item == null)
                return null;

            StringBuilder SB = new StringBuilder();

            foreach (var fo in item.AsParents().Reverse())
            {
                SB.Append(fo.Name);
                if (fo.HasExtension)
                {
                    SB.Append(ExtensionNameSeperator);
                    SB.Append(fo.Extension);
                }
                SB.Append(PrimaryNameSeperator);
            }

            if (SB.Length > 0)
            {
                SB.Remove(SB.Length - 1, 1);
            }

            return SB.ToString();
        }

        public static string ToNodeName(this NameItem item)
        {
            return
                item == null ?
                    null :
                    item.HasExtension ?
                        item.Name.AppendNamePart(item.Extension, ExtensionNameSeperator) :
                        item.Name;
        }

        #endregion

        #region ToNameItem

        #region Public Interfaces

        public static NameItem ToNameItem(this string nodeName)
        {
            return ToNameItem(nodeName, true);
        }

        public static NameItem ToNameItem(this IEnumerable<string> nodeNames)
        {
            return ToNameItem(nodeNames, true);
        }

        public static NameItem ParseName(this string fullName)
        {
            return ParseName(fullName, true);
        }


        #endregion

        #region Function Implementations

        private static NameItem ToNameItem(this string nodeName, bool validate)
        {
            if (nodeName == null)
                throw new ArgumentNullException("nodeName", "node Name is null.");

            if (validate && !nodeName.VerifyNodeName())
                throw NameException.CreateKnownException(NameException.ExceptionReason.InvalidNodeName);

            var parts = nodeName.Split(ExtensionNameSeperatorArray);

            switch (parts.Length)
            {
                case 1:
                    return new NameItem(parts[0]);
                case 2:
                    return new NameItem(parts[0], parts[1]);
                default:
                    throw new NameException("Invalid Extension Name Format");
            }
        }

        private static NameItem ToNameItem(this IEnumerable<string> nodeNames, bool validate)
        {
            if (nodeNames == null)
                return null;

            string first = nodeNames.FirstOrDefault();

            if (first == null)
                return null;

            NameItem result = first.ToNameItem(validate);

            foreach (var fo in nodeNames.Skip(1))
            {
                result.Child = fo.ToNameItem(validate);
                result = result.Child;
            }

            return result;
        }

        private static NameItem ParseName(this string fullName, bool validate)
        {
            if (validate && !fullName.VerifyName())
                throw NameException.CreateKnownException(NameException.ExceptionReason.InvalidName);

            var names = fullName.SplitToNameComponent();

            return names.ToNameItem();

        }

        #endregion

        #endregion

        #region NameItem Operations

        public static NameItem Child(this NameItem item, string name)
        {
            return Child(item, ToNameItem(name));
        }

        public static NameItem Child(this NameItem item, NameItem child)
        {
            item.Child = child;
            return child;
        }

        public static NameItem Extension(this NameItem item, string extension)
        {
            item.Extension = extension;
            return item;
        }

        public static NameItem ToParent(this NameItem item, int level = -1)
        {
            if (item == null)
                throw new ArgumentNullException("item", "item is null.");

            level = level == -1 ? int.MaxValue : level;

            for (int fo = 0; fo < level && item.HasParent; fo++, item = item.Parent)
            { }

            return item;
        }

        public static NameItem ToChild(this NameItem item, int level = -1)
        {
            if (item == null)
                throw new ArgumentNullException("item", "item is null.");

            level = level == -1 ? int.MaxValue : level;

            for (int fo = 0; fo < level && item.HasChild; fo++, item = item.Child)
            { }

            return item;
        }

        #endregion

        #region Verification

        public static bool VerifyNodePrimaryName(this string text)
        {
            return
                text != null &&
                !text.ContainsAny(NameSpeartorArray);
        }

        public static bool VerifyExtensionName(this string text)
        {
            return
                text == null ||
                !text.ContainsAny(NameSpeartorArray);
        }

        public static bool VerifyNodeName(this string text)
        {
            return
                text != null &&
                !text.ContainsAny(PrimaryNameSeperatorArray) &&
                !text.ContainsMoreThan(ExtensionNameSeperator);
        }

        public static bool VerifyName(this string text)
        {
            if (text == null)
                return false;

            var components = text.SplitToNameComponent();
            foreach (var fo in components)
            {
                if (!VerifyNodeName(fo))
                    return false;
            }

            return true;
        }

        #endregion

        #endregion

        #region Support Methods

        private static string AppendNamePart(this string main, string appended, char seperator)
        {
            return string.Format(HierarchyNameTemplate, main, appended, seperator);
        }

        private static string[] SplitToNameComponent(this string name)
        {
            return name.Split(PrimaryNameSeperatorArray);
        }

        private static bool ContainsAny(this string text, params char[] chars)
        {
            Debug.Assert(text != null, "text is null");

            foreach (var fo in chars)
            {
                if (text.Contains(fo))
                    return true;
            }

            return false;
        }

        private static bool ContainsMoreThan(this string text, char targetChar, int maxOccurs = 1)
        {
            int currentCount = 0;
            foreach (var fo in text)
            {
                if (fo == targetChar)
                {
                    currentCount++;
                    if (currentCount > maxOccurs)
                        return true;
                }
            }

            return false;
        }

        #endregion
    }
}
